// This kernel perform vectorized floating-point addition/multiplication
// to demonstrate how parallel processing can accelerate computation
// 2020.06.17 by wangdong@bjtu.edu.cn
//#include "ap_int.h"
//#include <stdio.h> 


#include "custypedef.h"

//--------------------- Baseline -----------------------//

extern "C" {
void coreConv(
	REFERENCE_STREAM(k2k<vec_type>, 16, weight_channels), 
	REFERENCE_STREAM(k2k<vec_type>, 16, img_channels),
	REFERENCE_STREAM(k2k<DATA_TYPE>, 16, result_channels)
)
{
	#pragma HLS interface ap_ctrl_none port=return // Special pragma for freerunning kernel
	#pragma HLS INTERFACE axis  port = weight_channels depth=16
	#pragma HLS INTERFACE axis  port = img_channels depth=16
	#pragma HLS INTERFACE axis  port = result_channels depth=256
	DATA_TYPE img[VEC_SIZE];
	DATA_TYPE knl[VEC_SIZE];
	DATA_TYPE mul[VEC_SIZE];
	DATA_TYPE acc[DATA_SIZE_W - DATA_SIZE_K + 1];
	const int bsize = (int)(5*5/VEC_SIZE)*VEC_SIZE+1;
	DATA_TYPE tmp = 0;
	volatile int convcnt = 0;
	// volatile int convoutputcnt = 0;

    for(uint i=0; i<DATA_SIZE_M; i++){
		#pragma HLS PIPELINE off
		for(uint h = 0; h < DATA_SIZE_H - DATA_SIZE_K + 1; h++){
			#pragma HLS PIPELINE off
			for(uint w=0; w<(DATA_SIZE_W - DATA_SIZE_K + 1); w++){
				#pragma HLS PIPELINE off
				tmp = 0;
				for(int n = 0; n < (DATA_SIZE_N); n++){
					//#pragma HLS PIPELINE off
					for(uint j=0; j<DATA_SIZE_K; j++){
						//#pragma HLS PIPELINE off
						k2k<vec_type> _trans_img;
						_trans_img = img_channels.read();

						k2k<vec_type> _trans_knl;
						_trans_knl = weight_channels.read();

						for(uint s = 0; s<VEC_SIZE; s++){
							#pragma HLS unroll
							img[s] = _trans_img.data(GET_BIT(img[s])*(s+1)-1, GET_BIT(img[s])*s);
						}

						for(uint s = 0; s<VEC_SIZE; s++){
							#pragma HLS unroll
							knl[s] = _trans_knl.data(GET_BIT(DATA_TYPE)*(s+1)-1, GET_BIT(DATA_TYPE)*s);
						}
								
						for(int k=0; k<VEC_SIZE; k++){
							#pragma HLS unroll
							mul[k]= knl[k] * img[k];
						}

						for(int k=0; k<VEC_SIZE; k++){
							#pragma HLS unroll
							tmp += mul[k];
						}
						++convcnt;
					}
				}
				k2k<DATA_TYPE> _trans_acc;
				_trans_acc.data(GET_BIT(tmp)-1, 0) = tmp;
				result_channels.write(_trans_acc);
				// ++convoutputcnt;
			}
		}
	}
}
}

